#!/usr/bin/env python

import logging
import os
import copy
import multiprocessing
import subprocess
import select
import glob

#Local imports
from loghelper import log_cmd
from config import split_strip

def chroot_mount(log, config, env):
	mobiemu_in_chroot = config.get('directories','chroot_bindpoint')
	for rootpoint in split_strip(',', config.get('directories','chroot_rootpoints')):
		for mountpoint in glob.glob(rootpoint):
			log.info("Mounting with --bind in %s", mountpoint)
			safe_exec(['mount','--bind',config.get('directories','main'),mountpoint+"/"+mobiemu_in_chroot], log, config, env, chdir=False)

def chroot_umount(log, config, env):
	mobiemu_in_chroot = config.get('directories','chroot_bindpoint')
	for rootpoint in split_strip(',', config.get('directories','chroot_rootpoints')):
		for mountpoint in glob.glob(rootpoint):
			log.info("Unmounting %s", mountpoint)
			safe_exec_ignore_errors(['umount',mountpoint + "/" + mobiemu_in_chroot], log, config, env, chdir=False)

def remove_devices(total_nodes, use_chroot, cfg, log, environment):
	"""
	Clean up any devices that have not been turned off, this can be run manually with the '-c' or '--cleanup' command to Run
	"""

	for n in range(1,total_nodes+1):
		tapdev='tap-'+str(n)
		brdev='br-'+str(n)
		node='node'+str(n)
		lxcconfig='/tmp/lxc-'+str(n)+'.conf'

		log.info("Removing bridge {} and tap device {} for node {}".format(brdev,tapdev,node))

		# stop lxc
		log.debug('Stopping LXC for {}'.format(node))
		safe_exec_ignore_errors(['lxc-stop','-n',node], log, cfg, environment)
		safe_exec_ignore_errors(['lxc-kill','-n',node], log, cfg, environment)
		safe_exec_ignore_errors(['lxc-destroy','-n',node], log, cfg, environment)

		# remove bridge and tap device
		log.debug('Removing tapdevices, and bridges')
		safe_exec_ignore_errors(['ifconfig',brdev,'down'], log, cfg, environment)
		safe_exec_ignore_errors(['brctl','delif',brdev,tapdev], log, cfg, environment)
		safe_exec_ignore_errors(['brctl','delbr',brdev], log, cfg, environment)
		safe_exec_ignore_errors(['ifconfig',tapdev,'down'], log, cfg, environment)
		safe_exec_ignore_errors(['tunctl','-d',tapdev], log, cfg, environment)

		# unlink config file
		try:
			if os.path.exists(lxcconfig):
				os.unlink(lxcconfig)
		except:
			log.exception("Unable to unlink %s", lxcconfig)

	# remove any remaining links
	links = subprocess.Popen(['ip','link','show'],stdout=subprocess.PIPE,close_fds=True)
	for link in links.stdout:
		if link.find('veth') > 0 or link.find('tap-') > 0:
			device = link.split(':')[1].strip()
			log.info("Device  %s still up. Removing...", device)
			safe_exec_ignore_errors(['ip','link','del',device], log, cfg, environment) # no point in using safe_exec here

	if os.path.ismount('/cgroup'):
		log.info("Unmounting /cgroup")
		safe_exec_ignore_errors(['umount','/cgroup'], log, cfg, environment)
		if os.path.exists('/cgroup'):
			try:
				log.debug('Removing /cgroup folder')
				os.rmdir('/cgroup')
			except:
				log.exception("Unable to remove /cgroup")

def safe_exec_ignore_errors(commands, log, config, environment, simulator_core=False, chdir=False):
	"""
	Execute command and ignore errors which might surface
	"""
	try:
		safe_exec(commands, log, config, environment,  simulator_core=simulator_core,chdir=chdir)
	except Exception,err:
		log.debug("%s (ignored)" % err)
		pass
	
def safe_exec(cmd, log, config, environment, nodeid='0', simulator_core=False,use_shell=False,chdir=False, select_time=False):
	log.debug("Arguments to Safe_exec: %s, %s, %s, %s, %s", cmd, log, config, environment, nodeid)

	commands = copy.copy(cmd) 

	log_prefix = commands[0]

	if chdir:
		if os.path.exists(commands[0]):
			needs_prefix = True
		else:
			needs_prefix = False
		dumps_dir = config.get('directories','dumps')

		old_dir = os.getcwd()
		os.chdir(dumps_dir)

		if needs_prefix:
			commands[0] = old_dir + '/' + commands[0]

		log.debug("Executing command in {}".format(dumps_dir).strip())

	# use last core for simulator, the others for normal tasks
	#if simulator_core == False: 
		#if multiprocessing.cpu_count() > 1: # By default, schedule on all but last core
			#cpu_core = "0-%s" % (multiprocessing.cpu_count()-2)
		#else:
			#cpu_core = "0"
		#log.debug("Executing on simulator core")
	#else:
		# schedule simulator tasks on last core
		#cpu_core = "%s" % (multiprocessing.cpu_count()-1)

	#commands[0:0] = ['nice','-n',niceness]
	if simulator_core:
		if config.has_option('ns3', 'taskset'):
			taskset = config.get('ns3', 'taskset')
		else:
			taskset = '0'
		commands[0:0] = ['taskset','-c',taskset]

	log.debug('Creating environment')
	if (nodeid != '0'):
		_env = environment[int(nodeid)]
	else:
		_env = {}
	_env.update(environment[0])

	# run shell commands as strings, not dicts
	if (use_shell):
		commands = ' '.join(commands)

	log.debug("{}".format(commands))

	try:
		child = subprocess.Popen(commands,stdout=subprocess.PIPE, bufsize=-1, stderr=subprocess.PIPE, close_fds=True, shell=use_shell,env=_env)
	
	finally:
		if chdir:
			os.chdir(old_dir)

	rs = [child.stdout,child.stderr]

	while rs:
		try:
			if not select_time:
				rlist,wlist,xlist = select.select(rs,[],[])
			else:
				rlist,wlist,xlist = select.select(rs,[],[], 10)
			#Wait 10 seconds if nothing has been made available move on
			if not rlist:
				break
		except select.error, e:
			if e.args[0] == error.EINTR:
				continue
			raise

		if child.stdout in rlist:
			line = child.stdout.readline()
			if line == "":
				child.stdout.close()
				rs.remove(child.stdout)
			else:
				log_cmd(log, log_prefix, line.strip())

		if child.stderr in rlist:
			line = child.stderr.readline()
			if line == "":
				child.stderr.close()
				rs.remove(child.stderr)
			else:
				log.error("{}:{}".format(log_prefix, line.strip()))
	
	child.wait()

	if child.poll() == None:
		raise Exception("Command still running after wait(). This should not happen.")

	if child.poll() != 0:
		raise Exception("Command exited with error %s" % child.poll())
	
def safe_exec_fork(commands, log, config, env, nodeid='0', simulator_core=False, chdir=False, use_shell=False): 
	process = multiprocessing.Process(target=safe_exec, args=(commands, log, config, env, nodeid, simulator_core, use_shell, chdir,))
	process.start()
	return process